
/**
 * @file
 * @brief
 * @author
 * @date
 * @note
 * @see
 * @version $Id$
**/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>

#include "public.h"
#include "fdwatch.h"
#include "sockio.h"
#include "handle.h"
#include "timer.h"
#include "logout.h"
#include "init_msg.h"

#define CFG_FILE	"./learnmore.cfg"
#define DEF_PORT	30303
#define DEF_TIMEOUT	10

static int readcfg();
static int read_date(struct user_session_t *session);
static int rewrite_data(struct user_session_t *session);
static struct user_session_t * getsessionbyconn(int conn);
static int run();
static int sedule();

// 读取配置文件,结果保存到全局变量 global_session 中.
static int readcfg()
{
	/*
	char name[32];
	char value[32];
	char line[128];

	FILE *fp = fopen(CFG_FILE, "r");
	if(fp==NULL) {
		perror("fopen() error");
		return -1;
	}

	while(feof(fp) == 0) {
		memset(line, '\0', sizeof(line));
		fgets(line, sizeof(line)-1, fp);

		line[strlen(line)-1]='\0';
		if(strlen(line) <= 0) {
			continue;
		}

		bzero(name, sizeof(name));
		bzero(value, sizeof(value));

		if(sscanf(line, "%[a-z]s=%[0-9]s", name, value) == -1) {
			perror("fscanf() error");
			continue;
		}
		if(strcmp(name, "ip")==0){
			strncpy(global_session.ip, value, sizeof(global_session.ip)-1);
		}
		if(strcmp(name, "port") == 0) {
			global_session.port = (short)atoi(value);
		}
	}
	*/
	return 0;
}

// 产生一个随机数.
unsigned long get_rand_num()
{
	srand((unsigned int)(time(NULL)*65535));
	return 13579+rand();
}

// 添加一个用户连接.
int add_user_session(int conn)
{
	LOG_ENTER();

	if(g_user_cnt >= DEF_USERMAX) {
		return -1;
	}

	g_user_list[g_user_cnt].conn=conn;
	g_user_cnt++;

	return 0;
}

// 删除一个用户 session
int del_user_session(int conn)
{
	int i;

	LOG_ENTER();

	for(i=0;i<g_user_cnt;i++) {
		if(g_user_list[i].conn==conn) {
			del_user_from_all_group(g_user_list[i].user_info.user_id);

			close(conn);
			bcopy(&g_user_list[g_user_cnt-1], &g_user_list[i], sizeof(struct user_session_t));
			bzero(&g_user_list[g_user_cnt-1], sizeof(struct user_session_t));
			g_user_cnt--;
			break;
		}
	}

	return 0;
}

// 添加一个群组 session
int add_group_session(struct group_info_t *group_info)
{
	LOG_ENTER();

	if(g_group_cnt >= DEF_GROUPMAX) {
		return -1;
	}

	bcopy(&g_group_list[g_group_cnt].group_info, group_info, sizeof(struct group_info_t));
	g_group_cnt++;

	return 0;
}

// 删除一个群组 session
int del_group_session(unsigned long group_id)
{
	int i,j;

	LOG_ENTER();

	// 查找群组
	for(i=0;i<g_group_cnt;i++) {
		if(g_group_list[i].group_info.group_id != group_id) {
			continue;
		}
		// 删除群组里的人.
		for(j=0; j<DEF_G_U_MAX; j++) {
			if(g_group_list[i].user_list[j]->conn > 0) {
				del_user_session(g_group_list[i].user_list[j]->conn);
			}
		}
		// 清除群组信息.
		bcopy(&g_group_list[i], &g_group_list[g_group_cnt-1], sizeof(struct group_session_t));
		bzero(&g_group_list[g_group_cnt-1], sizeof(struct group_session_t));
		g_group_cnt--;

		break;
	}

	return 0;
}

// 根据用户ID查找用户SESSION.
struct user_session_t *get_usersession_by_id(unsigned long id)
{
	for(int i=0; i<g_user_cnt; i++) {
		if(g_user_list[i].user_info.user_id == id) {
			return &g_user_list[i];
		}
	}

	return NULL;
}

// 根据组ID查找组SESSION.
struct group_session_t * get_groupsession_by_id(unsigned long id)
{
	for(int i=0; i<g_group_cnt; i++) {
		if(g_group_list[i].group_info.group_id == id) {
			return &g_group_list[i];
		}
	}

	return NULL;
}

// 向组中添加一用户.
int add_user_to_group(unsigned long group_id, struct user_session_t *session)
{
	struct group_session_t *group_session = get_groupsession_by_id(group_id);

	LOG_ENTER();

	if(group_session->user_cnt >= DEF_G_U_MAX) {
		return -1;
	}

	group_session->user_list[group_session->user_cnt] = session;
	group_session->user_cnt++;

	return 0;
}

// 从群中删除一用户.
int del_user_from_group(unsigned long group_id, unsigned long user_id)
{
	struct group_session_t *group_session = get_groupsession_by_id(group_id);
	//struct user_session_t *user_sessions = group_session->user_sessions;

	LOG_ENTER();

	for(int i=0; i<group_session->user_cnt; i++) {
		if(group_session->user_list[i]->user_info.user_id == user_id) {
			group_session->user_list[i] = group_session->user_list[group_session->user_cnt-1];
			group_session->user_list[group_session->user_cnt-1] = NULL;
			group_session->user_cnt--;
			break;
		}
	}

	return 0;
}

// 从系统中删除一用户.
int del_user_from_all_group(unsigned long user_id) 
{
	int i;

	LOG_ENTER();

	// 查找群组
	for(i=0;i<g_group_cnt;i++) {
		del_user_from_group(g_group_list[i].group_info.group_id, user_id);
	}

	return 0;
}

// 读取用户连接数据放入接收队列.
static int read_date(struct user_session_t *session)
{
	int len;

	LOG_ENTER();

	if(session->recvlen < DEF_BUFMAX) {
		len = read(session->conn, session->recvbuf+session->recvlen, DEF_BUFMAX-session->recvlen-1);
		if(len == -1) {
			perror("read() error");
			return -1;
		}
		session->recvlen+=len;
	}
	session->recvbuf[session->recvlen]='\0';

	LOGOUT(1, "read data len [%d]\n", len);

	return len;
}

// 打包发送.
int pack_write(struct user_session_t *session, int type, void *data, int len)
{
	struct proto_head_t *header;
	char pkg_data[10240];

	LOG_ENTER();

	if(sizeof(struct proto_head_t)+len >= sizeof(pkg_data)) {
		LOGOUT(1, "data too long [%d]\n", len);
		return -1;
	}

	header = (struct proto_head_t *)pkg_data;

	// 包头.
	header->magic = MAGICNUM;
	header->ver = VERNUM;
	header->num	= type;
	header->flag = 0;
	header->len = len;

	bcopy(data, pkg_data+sizeof(struct proto_head_t), len);

	return write_data(session, pkg_data, sizeof(struct proto_head_t)+len);
}

// 处理完用户消息后,发送最终结果.
int write_data(struct user_session_t *session, char *data, int len)
{
	int sndlen = 0;

	LOG_ENTER();

	// 发送一次,如有剩余,下次再发.
	sndlen = write(session->conn, data, len);
	if(sndlen == -1) {
		perror("1 write() error");
		return -1;
	}

	if(sndlen < len) {
		bcopy(data+sndlen, session->sendbuf+session->sendlen, len-sndlen);
		session->sendlen += (len-sndlen);

		fd_add(session->conn, DEF_WFD);
	}

	return 0;
}

// 可写时,发送缓存的数据.
static int rewrite_data(struct user_session_t *session)
{
	// 向用户发送数据.
	int sendlen = 0;

	LOG_ENTER();

	if(session->sendlen > 0) {
		sendlen = write(session->conn, session->sendbuf, session->sendlen);
		if(sendlen == -1) {
			perror("2 write() error");
			session->sendlen = 0;
			return -1;
		}

		if(sendlen < session->sendlen) {
			memmove(session->sendbuf, session->sendbuf+sendlen, session->sendlen-sendlen);

			fd_add(session->conn, DEF_WFD);
		}

		session->sendlen-=sendlen;
	}

	return 0;
}

// 通过客户连接,得到客户的 session
static struct user_session_t * getsessionbyconn(int conn)
{
	int i;

	for(i=0; i<g_user_cnt; i++) {
		if(g_user_list[i].conn == conn) {
			return &g_user_list[i];
		}
	}

	return NULL;
}

// 主处理函数
static int run()
{
	// 初始化描述符集超时时间.
	fd_init(DEF_TIMEOUT);

	// 初始化计时器.
	timer_init();

	// 初始化消息句柄数组.
	msg_init();

	// 添加所有处理的消息.
	inithandler();

	// 创建监听端口.
	global_session.sockfd = create_listen(NULL, global_session.port);
	if(global_session.sockfd < 0) {
		return -1;
	}

	fd_add(global_session.sockfd, DEF_RFD);

	signal(SIGPIPE, SIG_IGN);

	// 循环处理连接.
	while (1) {
		// 一次监听端口处理.
		sedule();

		// 执行一次计时器函数.
		timer_exec();
	}

	return 0;
}

// 一次监听端口处理.
static int sedule()
{
	int num;
	int conn;
	struct user_session_t *session;

	// 监听端口
	num = fd_watch();
	if(num <= 0) {
		return 0;
	}

	// 处理可读连接.
	while((conn = fd_get(DEF_RFD)) > 0) {
		// 新连接.
		if(conn == global_session.sockfd) {
			int newconn;

			newconn = accept_conntion(conn);
			if(newconn <= 0) {
				continue;
			}

			if(add_user_session(newconn) != -1) {
				fd_add(newconn, DEF_RFD);
			}

			continue;
		}

		// 接收数据.
		session = getsessionbyconn(conn);
		if(session == NULL){
			continue;
		}

		if(read_date(session) <= 0) {
			fd_del(session->conn);
			del_user_session(session->conn);
			continue;
		}

		msg_exec(session);
	};

	// 处理可写连接.
	while((conn = fd_get(DEF_WFD)) > 0) {
		session = getsessionbyconn(conn);
		if(session == NULL){
			continue;
		}

		if(rewrite_data(session) == -1) {
			fd_del(session->conn);
			del_user_session(session->conn);
			continue;
		}
	};

	return 0;
}

int main(int argc, char *argv[])
{
	global_session.port = (short)DEF_PORT;

	// 初始化日志系统.
	LOGOPEN(argv[0], 9, LOGSTDOUT);

	// 读取配置文件.
	readcfg();

	// 启动处理函数.
	run();

	return 0;
}

